package com.zte.claa.inficombo.csa.app.service.gprs;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.mina.core.future.WriteFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zte.claa.inficombo.csa.app.config.GPRSDevConnProps;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.service.csa.impl.BaseCacheManager;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSDevCommand;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSDevRegRsp;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMessage;
import com.zte.claa.inficombo.csa.app.service.gprs.bean.GPRSMsgHead;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevConnSession;
import com.zte.claa.inficombo.csa.app.service.gprs.mina.GPRSDevServerManager;
import com.zte.claa.inficombo.csa.app.service.nbiot.IotLocUpdataProcessService;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserContainer;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserGPRSUpdata;

@Service("gprs")
public class GPRSDevServiceImpl implements GPRSDevService, InitializingBean, DisposableBean {
	// 终端数据上报消息的cmd
	public static final String UPDATA_CMD = "updata";
		
	// 日志.
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	// 连接参数.
	@Autowired
	private GPRSDevConnProps connProps;
	
	@Autowired
	private BaseCacheManager cacheManager;
	
	@Autowired
	private ParserContainer parseContainer;
	
	@Autowired
	private IotLocUpdataProcessService locUpdataProcessService;
	
	// 连接管理服务器
	private GPRSDevServerManager connSvrManager = null;
	
	// 上行数据处理线程池.
	private ExecutorService dataRptExecSvr;
	
	private ParserGPRSUpdata parseGprsUpdate;
	
	@Override
	public void afterPropertiesSet() throws Exception {
		if(this.connProps.isSwitchon() == false){
			logger.warn("GPRSDevService is set off.");
			return;
		}
		this.parseGprsUpdate = new ParserGPRSUpdata();
		this.parseGprsUpdate.setParserContainer(parseContainer);
		this.parseGprsUpdate.setLocUpdataProcessService(this.locUpdataProcessService);
		try{			
			// 上行消息分发线程池.
			RejectedExecutionHandler faultHandler = new ThreadPoolExecutor.DiscardPolicy();
			// 此时是无界队列LinkedBlockingQueue，maxCoreNum参数无效...
			this.dataRptExecSvr = new ThreadPoolExecutor(this.connProps.getUpdealermaxnum(), 
				this.connProps.getUpdealermaxnum(), 120, TimeUnit.MILLISECONDS, 
    			new LinkedBlockingQueue<Runnable>(this.connProps.getMsgqueuesize()), faultHandler);
			// 初始化connserver.
			this.connSvrManager = new GPRSDevServerManager(this.connProps, this);
			this.connSvrManager.start();
			this.parseGprsUpdate.setGprsDevService(this);
			logger.info("GPRSDevService init ok!");
		}catch(Exception e){
			logger.error("GPRSDevService init failed!", e);
			this.destroy();
		}
	}
	
	               
	@Override
	public void destroy() throws Exception {
		try{
			if(this.connSvrManager != null) {
				this.connSvrManager.stop();
				this.connSvrManager = null;
			}
			if(this.dataRptExecSvr != null){
				this.dataRptExecSvr.shutdown();
				this.dataRptExecSvr = null;
			}
		}catch(Exception e){
			logger.error("destroy failed!", e);
		}
	}
	
	@Override
	public void onGPRSDevMsgReport(GPRSDevConnSession gprssession, GPRSMessage message) {
		if(this.dataRptExecSvr != null){
			int msgtp = message.getHead().getMsgtype();
			switch(msgtp){
			case GPRSMsgHead.GPRS_REGREQ:
				processGPRSRegReq(gprssession,message);
				break;
			case GPRSMsgHead.GPRS_HBREQ:
				break;
			case GPRSMsgHead.GPRS_UPLINK:
				if(GPRSDevConnSession.DEV_REGED == gprssession.getProcstat()){
					processGPRSReport(gprssession,message);
				}else{
					logger.warn("Device is not registered!");
				}
				break;
			default:
				logger.warn("Unknow message type: tp=" + msgtp + ", msg=" + message);
				break;
			}			
		} else {
			logger.warn("Rpt message dispatcher pool is null!");
		}
		return;
	}
	
	private void processGPRSRegReq(GPRSDevConnSession gprsconn, GPRSMessage msg){
		try{
			// 注册消息 
			String deveui =  msg.getHead().getDeveui();
			DevBase devbase = this.cacheManager.getDev(deveui);
			GPRSDevRegRsp regRsp = new GPRSDevRegRsp();
			if(null == devbase){
				msg.setErrtype(GPRSMessage.ERR_DEVNOTEXIST);
			}			
			if(GPRSMessage.ERR_DEVNOTEXIST == msg.getErrtype()
			   || GPRSMessage.ERR_AUTHFAIL == msg.getErrtype()
			   || GPRSMessage.ERR_CRCCHECK == msg.getErrtype())
			{
				regRsp.setResult((byte)1);
			}else{
				//重复注册也响应成功消息
				regRsp.setResult((byte)0);
			}
			regRsp.setDeveui(deveui);
			regRsp.setReason((byte)msg.getErrtype());
			GPRSDevCommand gprsDevCmd = buildGPRSRegAck(regRsp);
			this.sendCommand(deveui, gprsDevCmd);
			
			if(0 != regRsp.getResult()){
				//释放session
				//this.closeSession(deveui);
				gprsconn.setProcstat(GPRSDevConnSession.DEV_UNREG);
			}else{
				gprsconn.setProcstat(GPRSDevConnSession.DEV_REGED);
				devbase.setNetworktype(DevBase.NETTYPE_GPRS);
				gprsconn.setIccid(msg.getHead().getIccid());
				gprsconn.setRssi(msg.getHead().getRssi());
			}
		}catch(Exception e){
			logger.error("processGPRSRegReq failed: "+msg, e);
		}
	}
	
	private void processGPRSReport(GPRSDevConnSession gprsconn, GPRSMessage msg){
		try{
			if(null == msg){
				logger.error("processGPRSReport failed, msg is null!");
				return;
			}
			this.addGPRSUpdataToPool(msg);
		}catch(Exception e){
			logger.error("processGPRSReport failed: "+msg, e);
		}
	}
	
	private void addGPRSUpdataToPool(GPRSMessage msg){
		this.dataRptExecSvr.submit(new Runnable(){
			@Override
			public void run() {
				try {
					parseGprsUpdate.parseGPRSData(msg);
				} catch (Exception e) {
					logger.error("parseGPRSData failed: "+msg, e);					
				}
			}
		});
	}
	
	@Override
	public WriteFuture sendCommand(String deveui, GPRSDevCommand cmd) throws Exception {
		if(deveui == null || deveui.equals("")){
			logger.warn("GPRS sendCommand failed: deveui is null");
			throw new Exception("sendCommand failed: deveui is null!");
		}
		if(cmd == null){
			logger.warn("GPRS sendCommand failed: cmd is null");
			throw new Exception("GPRS sendCommand failed: cmd is null!");
		}
		DevBase dev = this.cacheManager.getDev(deveui);
		if(dev == null){
			logger.warn("GPRSDev not exist: "+deveui);
			throw new Exception("GPRSDev not exist: "+deveui);
		}
		GPRSDevConnSession session = this.connSvrManager.getDeveuiSessionMap().get(deveui);
		if(null == session){
			return null;
		}
		return session.sendCommand(cmd);
	}
	
	
	public static GPRSDevCommand buildGPRSRegAck(GPRSDevRegRsp regRsp){
		if(null == regRsp){
			return null;
		}
		GPRSDevCommand gprsDevCmd = new GPRSDevCommand();
		gprsDevCmd.setCmdtype(regRsp.getMsgtype());
		gprsDevCmd.setDeveui(regRsp.getDeveui());
		byte[] payload = new byte[regRsp.getRegRspPayLen()];
		payload[0] = GPRSDevCommand.GPRS_REGRSP;
		payload[1] = regRsp.getResult();
		payload[2] = regRsp.getReason();
		gprsDevCmd.setPayload(payload);
		return gprsDevCmd;
	}
	
	@Override
	public void onExceptionReport(long sessionId, Throwable cause) {
		logger.error("Exception is reported: sessionId={}", sessionId, cause);
	}


	@Override
	public void onConnectionClose(long sessionId) {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void closeConnection(String deveui) throws Exception {
		this.connSvrManager.closeSessionByDeveui(deveui);
		
	}

	@Override
	public GPRSDevServerManager getConnSvrManager() {
		return connSvrManager;
	}
	
}
